﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Components.Color.Types;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core.Primitives;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Corrector.Types;

namespace RayDen.RayEngine.Corrector.Renderers
{
    public class PathSampleProcessor
    {
        private int maxPaths;
        private int processedSamples;
        public long statsRenderingStart, pass;
        public long statsTotalSampleCount;

        protected  RayEngineScene rayEngineScene;
        private CorrectorImageFilm pixelDevice;
        internal protected GlobalSamplingContext samplers;
        protected ImagePlaneSample[] Samples;

        protected List<PathSampler> paths;
        protected int firstPath, lastPath;
        private SampleBuffer<IColorType> sampleBuffer;

        protected internal Func<ImagePlaneSample[]> RequestSamplesFunc;


        public PathSampleProcessor(int maxpaths, RayEngineScene rayEngineScene, CorrectorImageFilm pixelDevice, GlobalSamplingContext samplers)
        {
            this.maxPaths = maxpaths;
            this.rayEngineScene = rayEngineScene;
            this.pixelDevice = pixelDevice;
            this.samplers = samplers;
            this.sampleBuffer = pixelDevice.GetFreeSampleBuffer();
            this.paths = new List<PathSampler>(maxPaths);
        }

      
        public void ReInit()
        {
            for (var i = 0; i < paths.Count; ++i)
                paths[i].InitPath(this);
            firstPath = 0;
            //film.Reset();

            statsRenderingStart = DateTime.UtcNow.Ticks / 1000;
            statsTotalSampleCount = 0;
        }


        public virtual PathSampler CreatePathSampler()
        {
            var ps = new PathSampler();
            ps.InitPath(this);
            return ps;
        }

        public ImagePlaneSample GetImageSample()
        {
            if (processedSamples < Samples.Length-1)
            {
                processedSamples++;
            }
            else
            {
                pass += 1;
                processedSamples = 0;
            }

            return Samples[processedSamples];
        }
        public void ClearPaths()
        {
            paths.Clear();
        }

        public void FillRayBuffer(RayBuffer rayBuffer)
        {
            if (paths.Count == 0)
            {
                // Need at least 2 paths
                paths.Add(this.CreatePathSampler());
                paths.Add(this.CreatePathSampler());
                paths.Add(this.CreatePathSampler());

                paths[0].InitPath(this);
                paths[1].InitPath(this);
                paths[2].InitPath(this);

                firstPath = 0;
            }

            bool allPathDone = true;
            lastPath = firstPath;
            for (; ; )
            {
                if (!paths[lastPath].FillRayBuffer(rayBuffer))
                {
                    // Not enough free space in the RayBuffer
                    allPathDone = false;
                    break;
                }

                lastPath = (lastPath + 1) % paths.Count;
                if (lastPath == firstPath)
                    break;
            }

            if (allPathDone)
            {
                var newPaths = 0;
                var maxNewPaths = maxPaths;

                for (; ; )
                {
                    newPaths++;
                    if (newPaths > maxNewPaths)
                    {
                        firstPath = 0;
                        lastPath = paths.Count - 1;
                        break;
                    }

                    var p = this.CreatePathSampler();
                    paths.Add(p);
                    p.InitPath(this);
                    if (!p.FillRayBuffer(rayBuffer))
                    {
                        firstPath = 0;
                        lastPath = paths.Count - 2;
                        break;
                    }
                }
            }
        }


        public void AdvancePaths(RayBuffer rayBuffer)
        {
#if VERBOSE
            try
            {
#endif
            for (int i = firstPath; i != lastPath; i = (i + 1) % paths.Count)
            {
                paths[i].Advance(rayBuffer, this.sampleBuffer);

                if (pass > 0)
                {
                    this.RequestSamples();
                }

                if (this.sampleBuffer.IsFull())
                {
                    this.statsTotalSampleCount += this.sampleBuffer.GetSampleCount();
                    // Splat all samples on the film
                    this.pixelDevice.SplatSampleBuffer(true, sampleBuffer);
                    this.sampleBuffer = this.pixelDevice.GetFreeSampleBuffer();
                }
            }

            firstPath = (lastPath + 1) % paths.Count;

#if VERBOSE
            }
            catch (Exception ex)
            {
                Tracer.TraceLine(ex.Message);
                Tracer.TraceLine(ex.StackTrace);
                throw;
            }
#if PATH_VERBOSE
            Console.WriteLine("{0} Created Paths / {1} Splatted Paths / {2} Instances currently", createdCount, splattedCount, this.paths.Count);
#endif
#endif

        }

        private void RequestSamples()
        {
            pass = 0;
            //this.
        }

        public void SetSamples(ImagePlaneSample[] imagePlaneSample)
        {
            this.Samples = imagePlaneSample;
            processedSamples = 0;
        }
    }
}
